package com.fenqing.core;

import com.fenqing.annotations.Execute;
import com.fenqing.utils.Encoder;
import com.fenqing.utils.EncoderFactory;
import lombok.SneakyThrows;

import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.lang.reflect.Type;

/**
 * @author fenqing
 */
public abstract class Topic {

    private EncoderFactory encoderFactory = EncoderFactory.getInstance();

    protected Object[] objects;

    private Method execMethod;

    public Object[]  getObjects(){
        return objects;
    }

    public Topic(){
        Class<? extends Topic> aClass = this.getClass();
        Method[] declaredMethods = aClass.getDeclaredMethods();
        for (Method declaredMethod : declaredMethods) {
            Execute annotation = declaredMethod.getAnnotation(Execute.class);
            if (annotation != null) {
                execMethod = declaredMethod;
            }
        }
    }

    public Topic(String... args){
        this();
        if (execMethod != null && args != null) {
            init(args);
        }
    }

    private void init(String... args){
        this.objects = new Object[execMethod.getParameterCount()];
        Parameter[] parameters = execMethod.getParameters();
        for (int i = 0; i < parameters.length; i++) {
            Parameter parameter = parameters[i];
            Type parameterizedType = parameter.getParameterizedType();
            Encoder<String, ?> encoder = encoderFactory.getEncoder(parameterizedType);
            objects[i] = encoder.encoder(args[i]);
        }
    }

    @SneakyThrows
    @SuppressWarnings("all")
    public Object execute() {
        Execute annotation = execMethod.getAnnotation(Execute.class);
        if (annotation != null) {
            boolean aVoid = annotation.isVoid();
            if (aVoid) {
                int i = annotation.voidReturnIndex();
                execMethod.invoke(this, this.objects);
                Class<? extends Encoder> aClass1 = annotation.voidReturnEncoder();
                if (!aClass1.equals(Object.class)) {
                    Encoder encoder = aClass1.newInstance();
                    return encoder.encoder(this.objects[i]);
                } else {
                    return this.objects[i];
                }
            } else {
                return execMethod.invoke(this, this.objects);
            }
        }
        return null;
    }

    @SuppressWarnings("all")
    public <T> T run() {
        long start = System.nanoTime();
        long start1 = System.currentTimeMillis();
        Object execute = execute();
        long end = System.nanoTime();
        long end1 = System.currentTimeMillis();
        System.out.println("耗时：" + (end - start) + "ns");
        System.out.println("耗时：" + (end1 - start1) + "ms");
        return (T) execute;
    }

    public <T> T run(String... args) {
        init(args);
        return run();
    }

    @SuppressWarnings("all")
    protected <T> T getObject(int index) {
        return (T) this.objects[index];
    }
}
